package cn.caplike.demo.shiro.realm;

import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * Description: Shiro 配置类.
 *
 * @author LiKe
 * @version 1.0.0
 * @date 2021-04-06 10:34
 */
@Configuration
public class ShiroConfiguration {

    /**
     * Description: 注入 Realm.<br>
     * Or:
     * <pre>
     * Description:
     * No bean of type 'org.apache.shiro.realm.Realm' found.
     * Action:
     * Please create bean of type 'Realm' or add a shiro.ini in the root classpath (src/main/resources/shiro.ini) or in the META-INF folder (src/main/resources/META-INF/shiro.ini).
     * </pre>
     *
     * @return cn.caplike.demo.shiro.quickstart.realm.AuthRealm
     * @author LiKe
     * @date 2021-04-06 11:17:01
     */
    @Bean("authRealm")
    public AuthRealm authRealm() {
        return new AuthRealm();
    }

    /**
     * Description: 注入 SecurityManager.<br>
     * Details: Shiro 的核心组件, 所有具体的交互都是通过 `SecurityManager` 进行控制, 它管理着所有 `Subject`, 且负责进行认证 (`Authenticator`) 和授权 (`Authorizer`) 及会话缓存 (`SessionManager`) 的管理.<br>
     * Or:
     * <pre>
     * Description:
     * Parameter 0 of method authorizationAttributeSourceAdvisor in org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration required a bean named 'authorizer' that could not be found.
     * Action:
     * Consider defining a bean named 'authorizer' in your configuration.
     * </pre>
     *
     * @param authRealm {@link AuthRealm}
     * @return org.apache.shiro.web.mgt.DefaultWebSecurityManager
     * @author LiKe
     * @date 2021-04-06 11:32:41
     * @see org.apache.shiro.spring.boot.autoconfigure.ShiroAnnotationProcessorAutoConfiguration#authorizationAttributeSourceAdvisor(SecurityManager)
     */
    @Bean("defaultWebSecurityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("authRealm") Realm authRealm) {
        final DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(authRealm);
        return defaultWebSecurityManager;
    }

    /**
     * Description: 注入 {@link ShiroFilterFactoryBean} (创建 ShiroFilter 的工厂 Bean).<br>
     * Details: 覆盖容器中的 {@link ShiroFilterFactoryBean}.<br>
     * Reference: {@code org.apache.shiro.spring.web.config.ShiroWebFilterConfiguration#shiroFilterFactoryBean()}
     *
     * @param securityManager {@link DefaultWebSecurityManager}
     * @return org.apache.shiro.spring.web.ShiroFilterFactoryBean
     * @author LiKe
     * @date 2021-04-06 12:02:09
     * @see org.springframework.beans.factory.FactoryBean
     * @see org.apache.shiro.web.servlet.ShiroFilter
     */
    @Bean("shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") SecurityManager securityManager) {
        final ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        /*
         * [ * ] FilterChainDefinitionMap:
         *       维护着 chainName 和 chainDefinition 的映射关系, chainDefinition 用于创建被 Shiro Filter 拦截的过滤器链.
         *       映射关系的每一个 Entry 都应该遵循定义在 FilterChainManager.createChain(String, String) 的格式,
         *       其中 map 的 key 为 chainName (如 URL 路径表达式), value 为逗号分隔的字符串 chainDefinition (字符串).
         *
         * [ ! ] chainDefinition 到真实 Filter 的映射过程:
         *       开发者置入的 FilterChainDefinitionMap 会在 ShiroFilterFactoryBean#getFilterChainDefinitionMap (createFilterChainManager() called by createInstance() (getObject())) 被使用,
         *       并被 DefaultFilterChainManager 用于添加到 NamedFilterList 中 (DefaultFilterChainManager#addToChain),
         *       在 addToChain 方法中, 会通过 getFilter(filterName) 获取真实的 Filter (从属性 filters 中),
         *       而在 DefaultFilterChainManager 的构造方法中, 会把默认的 DefaultFilter (org.apache.shiro.web.filter.mgt.DefaultFilter) 添加到 filters.
         */
        final Map<String, String> filterChainDefinitionMap = new HashMap<>();
        // filterChainDefinitionMap.put("/access/do", org.apache.shiro.web.filter.mgt.DefaultFilter.invalidRequest.name());
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

}
